package com.afollestad.commons.color;

import com.afollestad.commons.ResourceTable;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.GravityEnum;
import com.afollestad.materialdialogs.internal.MDButton;
import com.afollestad.materialdialogs.utils.ResUtil;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.global.configuration.Configuration;
import ohos.global.resource.solidxml.Theme;

import java.io.Serializable;
import java.util.Locale;

public class ColorChooserDialog extends CommonDialog implements Component.ClickedListener {

    private static final String TAG = ColorChooserDialog.class.getSimpleName();
    public static final String TAG_PRIMARY = "[MD_COLOR_CHOOSER]";
    public static final String TAG_ACCENT = "[MD_COLOR_CHOOSER]";
    public static final String TAG_CUSTOM = "[MD_COLOR_CHOOSER]";
    private Color[] colorsTop;
    private Color[][] colorsSub;
    private DirectionalLayout mDialogLayout;
    private ListContainer mListComponent;
    //private ContactDeviceListItemProvider mItemProvider;
    private Context mContext;
    private TableLayout tableLayout;
    private Text.TextObserver customColorTextWatcher;
    private Slider.ValueChangedListener customColorRgbListener;
    private Component colorChooserCustomFrame;
    private TextField customColorHex;
    private Component customColorIndicator;
    private Slider customSeekA;
    private Text customSeekAValue;
    private Slider customSeekR;
    private Text customSeekRValue;
    private Slider customSeekG;
    private Text customSeekGValue;
    private Slider customSeekB;
    private Text customSeekBValue;
    private int selectedCustomColor;
    private DirectionalLayout.LayoutConfig cvlayoutConfig;
    private boolean isinsub = false;
    private int topindex = 0;
    private int subindex = 0;

    private Text title;
    private MDButton positiveButton;
    private MDButton neutralButton;
    private MDButton negativeButton;
    DependentLayout buttonLayout;
    private TableLayout idTableLayout;
    private static ColorCallback callback;


    public ColorChooserDialog(Context context) {
        super(context);
        mContext = context;
        generateColors();
        initDialogLayout();
    }

    public static <E extends Component> E getComponent(Component root, int id) {
        if (root == null) {
            return null;
        }
        try {
            return (E) root.findComponentById(id);
        } catch (ClassCastException ex) {
            throw ex;
        }
    }

    private void generateColors() {
        Builder builder = getBuilder();

        if (builder.colorsTop != null) { //This is for custom colors
            colorsTop = builder.colorsTop;
            colorsSub = builder.colorsSub;
            return;
        }

        if (builder.accentMode) {
            colorsTop = ColorPalette.ACCENT_COLORS;
            colorsSub = ColorPalette.ACCENT_COLORS_SUB;
        } else {
            colorsTop = ColorPalette.PRIMARY_COLORS;
            colorsSub = ColorPalette.PRIMARY_COLORS_SUB;
        }
    }

    private void initDialogLayout() {
        mDialogLayout = (DirectionalLayout) LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_md_dialog_colorchooser, null, false);
        if (!(mDialogLayout instanceof ComponentContainer)) {
            return;
        }

        int preselectColor;
        boolean foundPreselectColor = false;

        if (getBuilder().setPreselectionColor) {
            preselectColor = getBuilder().preselectColor;
            if (preselectColor != 0) {
                for (int topIndex = 0; topIndex < colorsTop.length; topIndex++) {
                    if (colorsTop[topIndex].getValue() == preselectColor) {
                        foundPreselectColor = true;
                        topIndex(topIndex);
                        if (getBuilder().accentMode) {
                            subIndex(2);
                        } else if (colorsSub != null) {
                            findSubIndexForColor(topIndex, new Color(preselectColor));
                        } else {
                            subIndex(5);
                        }
                        break;
                    }

                    if (colorsSub != null) {
                        for (int subIndex = 0; subIndex < colorsSub[topIndex].length; subIndex++) {
                            if (colorsSub[topIndex][subIndex].getValue() == preselectColor) {
                                foundPreselectColor = true;
                                topIndex(topIndex);
                                subIndex(subIndex);
                                break;
                            }
                        }
                        if (foundPreselectColor) {
                            break;
                        }
                    }
                }
            }
        } else {
            preselectColor = Color.BLACK.getValue();
            foundPreselectColor = true;
        }


        title = (Text) mDialogLayout.findComponentById(ResourceTable.Id_md_title);
        title.setText(getTitle());

        positiveButton = (MDButton) mDialogLayout.findComponentById(ResourceTable.Id_md_buttonDefaultPositive);
        prepareButtonForUI(ResUtil.getString(mContext, getBuilder().doneBtn), positiveButton, GravityEnum.START.getTextAlignment(), DialogAction.POSITIVE, preselectColor);
        negativeButton = (MDButton) mDialogLayout.findComponentById(ResourceTable.Id_md_buttonDefaultNegative);
        prepareButtonForUI(ResUtil.getString(mContext, getBuilder().cancelBtn), negativeButton, GravityEnum.END.getTextAlignment(), DialogAction.NEGATIVE, preselectColor);
        neutralButton = (MDButton) mDialogLayout.findComponentById(ResourceTable.Id_md_buttonDefaultNeutral);
        prepareButtonForUI(ResUtil.getString(mContext, getBuilder().customBtn), neutralButton, GravityEnum.CENTER.getTextAlignment(), DialogAction.NEUTRAL, preselectColor);
        createTableLayout();
        if (getBuilder().allowUserCustom) {
            selectedCustomColor = preselectColor;
            colorChooserCustomFrame = mDialogLayout.findComponentById(ResourceTable.Id_md_colorChooserCustomFrame);
            customColorHex = (TextField) mDialogLayout.findComponentById(ResourceTable.Id_md_hexInput);
            customColorIndicator = mDialogLayout.findComponentById(ResourceTable.Id_md_colorIndicator);
            customSeekA = (Slider) mDialogLayout.findComponentById(ResourceTable.Id_md_colorA);
            customSeekAValue = (Text) mDialogLayout.findComponentById(ResourceTable.Id_md_colorAValue);
            customSeekR = (Slider) mDialogLayout.findComponentById(ResourceTable.Id_md_colorR);
            customSeekRValue = (Text) mDialogLayout.findComponentById(ResourceTable.Id_md_colorRValue);
            customSeekG = (Slider) mDialogLayout.findComponentById(ResourceTable.Id_md_colorG);
            customSeekGValue = (Text) mDialogLayout.findComponentById(ResourceTable.Id_md_colorGValue);
            customSeekB = (Slider) mDialogLayout.findComponentById(ResourceTable.Id_md_colorB);
            customSeekBValue = (Text) mDialogLayout.findComponentById(ResourceTable.Id_md_colorBValue);

            if (!getBuilder().allowUserCustomAlpha) {
                mDialogLayout.findComponentById(ResourceTable.Id_md_colorALabel).setVisibility(Component.HIDE);
                customSeekA.setVisibility(Component.HIDE);
                customSeekAValue.setVisibility(Component.HIDE);
                customColorHex.setHint("2196F3");
            } else {
                customColorHex.setHint("FF2196F3");
            }

            if (!foundPreselectColor) {
                // If color wasn't found in the preset colors, it must be custom
                toggleCustom();
            }
        }

        setContentCustomComponent(mDialogLayout);
    }

    private void toggleCustom() {
        if (tableLayout.getVisibility() == Component.VISIBLE) {
            title.setText(getBuilder().customBtn);
            neutralButton.setText(getBuilder().presetsBtn);
            negativeButton.setText(getBuilder().cancelBtn);
            tableLayout.setVisibility(Component.HIDE);
            colorChooserCustomFrame.setVisibility(Component.VISIBLE);
            customColorTextWatcher =
                    (s, start, before, count) -> {
                        try {
                            selectedCustomColor = Color.getIntColor("#" + s);
                        } catch (Exception e) {
                            selectedCustomColor = Color.BLACK.getValue();
                        }
                        RgbColor rgbColor = RgbColor.fromArgbInt(selectedCustomColor);
                        ShapeElement sp = new ShapeElement();
                        sp.setRgbColor(rgbColor);
                        customColorIndicator.setBackground(sp);
                        if (customSeekA.getVisibility() == Component.VISIBLE) {
                            int alpha = Color.alpha(selectedCustomColor);
                            customSeekA.setProgressValue(alpha);
                            customSeekAValue.setText(String.format(Locale.US, "%d", alpha));
                        }
                        customSeekR.setProgressValue(rgbColor.getRed());
                        customSeekG.setProgressValue(rgbColor.getGreen());
                        customSeekB.setProgressValue(rgbColor.getBlue());
                        isInSub(false);
                        topIndex(-1);
                        subIndex(-1);
                        invalidateDynamicButtonColors();
                    };

            customColorHex.addTextObserver(customColorTextWatcher);
            customColorRgbListener = new Slider.ValueChangedListener() {

                @Override
                public void onProgressUpdated(Slider slider, int progress, boolean fromUser) {
                    if (fromUser) {
                        if (getBuilder().allowUserCustomAlpha) {
                            int color =
                                    Color.argb(
                                            customSeekA.getProgress(),
                                            customSeekR.getProgress(),
                                            customSeekG.getProgress(),
                                            customSeekB.getProgress());
                            customColorHex.setText(String.format("%08X", color));
                        } else {
                            int color =
                                    Color.rgb(
                                            customSeekR.getProgress(),
                                            customSeekG.getProgress(),
                                            customSeekB.getProgress());
                            customColorHex.setText(String.format("%06X", 0xFFFFFF & color));
                        }
                    }
                    customSeekAValue.setText(String.format("%d", customSeekA.getProgress()));
                    customSeekRValue.setText(String.format("%d", customSeekR.getProgress()));
                    customSeekGValue.setText(String.format("%d", customSeekG.getProgress()));
                    customSeekBValue.setText(String.format("%d", customSeekB.getProgress()));
                }

                @Override
                public void onTouchStart(Slider slider) {
                }

                @Override
                public void onTouchEnd(Slider slider) {
                }
            };

            customSeekR.setValueChangedListener(customColorRgbListener);
            customSeekG.setValueChangedListener(customColorRgbListener);
            customSeekB.setValueChangedListener(customColorRgbListener);
            if (customSeekA.getVisibility() == Component.VISIBLE) {
                customSeekA.setValueChangedListener(customColorRgbListener);
                customColorHex.setText(String.format("%08X", selectedCustomColor));
            } else {
                customColorHex.setText(String.format("%06X", 0xFFFFFF & selectedCustomColor));
            }
        } else {
            title.setText(getBuilder().title);
            neutralButton.setText(getBuilder().customBtn);
            negativeButton.setText(getBuilder().cancelBtn);
            if (isInSub()) {
                negativeButton.setText(getBuilder().backBtn);
            } else {
                negativeButton.setText(getBuilder().cancelBtn);
            }
            tableLayout.setVisibility(Component.VISIBLE);
            colorChooserCustomFrame.setVisibility(Component.HIDE);
            customColorHex.removeTextObserver(customColorTextWatcher);
            customColorTextWatcher = null;
            customSeekR.setValueChangedListener(null);
            customSeekG.setValueChangedListener(null);
            customSeekB.setValueChangedListener(null);
            customColorRgbListener = null;
        }
    }

    private void prepareButtonForUI(CharSequence buttonText, MDButton button, int textAlignment, DialogAction action, int color) {
        button.setText(buttonText.toString());
        button.setTextAlignment(textAlignment);
        button.setTag(action);
        button.setTextColor(new Color(color));
        button.setClickedListener(this);
    }


    private int topIndex() {
        return topindex;
    }

    private void topIndex(int value) {
        if (value > -1) {
            findSubIndexForColor(value, colorsTop[value]);
        }
        topindex = value;
    }


    private void findSubIndexForColor(int topIndex, Color color) {
        if (colorsSub == null || colorsSub.length - 1 < topIndex) {
            return;
        }
        Color[] subColors = colorsSub[topIndex];
        for (int subIndex = 0; subIndex < subColors.length; subIndex++) {
            if (subColors[subIndex].getValue() == color.getValue()) {
                subIndex(subIndex);
                break;
            }
        }
    }

    private int subIndex() {
        if (colorsSub == null) {
            return -1;
        }
        return subindex;
    }

    private void subIndex(int value) {
        if (colorsSub == null) {
            return;
        }
        subindex = value;
    }

    private void invalidateDynamicButtonColors() {
        final Builder builder = getBuilder();
        if (builder.dynamicButtonColor) {
            int selectedColor = getSelectedColor();
            RgbColor rgbColor = RgbColor.fromArgbInt(selectedColor);
            if (Color.alpha(selectedColor) < 64
                    || (rgbColor.getRed() > 247
                    && rgbColor.getGreen() > 247
                    && rgbColor.getBlue() > 247)) {
                // Once we get close to white or transparent,
                // the action buttons and seek bars will be a very light gray.
                selectedColor = Color.getIntColor("#DEDEDE");
            }
            Color selectedColorObj = new Color(selectedColor);
            if (getBuilder().dynamicButtonColor) {
                positiveButton.setTextColor(selectedColorObj);
                negativeButton.setTextColor(selectedColorObj);
                neutralButton.setTextColor(selectedColorObj);
            }

            if (customSeekR != null) {
                if (customSeekA.getVisibility() == Component.VISIBLE) {
                    customSeekA.setProgressColor(selectedColorObj);
                }
                customSeekR.setProgressColor(selectedColorObj);
                customSeekG.setProgressColor(selectedColorObj);
                customSeekB.setProgressColor(selectedColorObj);
            }
        }
    }

    private int getSelectedColor() {

        int color = 0;
        if (subIndex() > -1) {
            color = colorsSub[topIndex()][subIndex()].getValue();
        } else if (topIndex() > -1) {
            color = colorsTop[topIndex()].getValue();
        } else if (selectedCustomColor != 0) {
            color = selectedCustomColor;
        }
        if (color == 0) {
            color = ResUtil.getColor(mContext, ResourceTable.Color_colorAccent);
        }
        return color;
    }


    private int getTitle() {
        Builder builder = getBuilder();
        int title;
        if (isInSub()) {
            title = builder.titleSub;
        } else {
            title = builder.title;
        }
        if (title == 0) {
            title = builder.title;
        }
        return title;
    }

    private void isInSub(boolean value) {
        isinsub = value;
    }

    private boolean isInSub() {
        return isinsub;
    }

    private int getCount() {
        if (isInSub()) {
            return colorsSub[topIndex()].length;
        } else {
            return colorsTop.length;
        }
    }


    private void prepareCircleviews() {

        for (int position = 0; position < getCount(); position++) {
            CircleView cv = new CircleView(mContext);
            cv.setLayoutConfig(cvlayoutConfig);
            tableLayout.addComponent((Component) cv);
            Color color = isInSub() ? colorsSub[topIndex()][position] : colorsTop[position];
            cv.setBackgroundColor(color);

            if (isInSub()) {
                cv.setSelected(subIndex() == position);
            } else {
                cv.setSelected(topIndex() == position);
            }
            cv.setTag(String.format("%d:%d", position, color.getValue()));
            cv.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component Component) {
                    if (Component.getTag() != null) {
                        final String[] tag = ((String) Component.getTag()).split(":");
                        final int index = Integer.parseInt(tag[0]);

                        if (isInSub()) {
                            subIndex(index);
                        } else {
                            topIndex(index);
                            if (colorsSub != null && index < colorsSub.length) {
                                negativeButton.setText(ResUtil.getString(mContext, getBuilder().backBtn).toUpperCase());
                                isInSub(true);
                            }
                        }
                        invalidateDynamicButtonColors();
                        invalidate();
                    }
                }
            });

            cv.setLongClickedListener(new Component.LongClickedListener() {
                @Override
                public void onLongClicked(Component component) {
                    if (component.getTag() != null) {
                        final String[] tag = ((String) component.getTag()).split(":");
                        final int color = Integer.parseInt(tag[1]);
                        ((CircleView) component).showHint(color);
                    }
                }
            });
        }
    }

    private void invalidate() {
        tableLayout.removeAllComponents();  //Remove previous components
        prepareCircleviews();
        if (title != null)
            title.setText(getTitle());
    }


    private int getrowcount() {
        if (isInSub()) {
            return 3;
        } else {
            return 5;
        }
    }

    private int getcolumncount() {
        if (isInSub()) {
            return 4;
        } else {
            return 4;
        }
    }

    private TableLayout createTableLayout() {
        tableLayout = (TableLayout) mDialogLayout.findComponentById(ResourceTable.Id_md_table_layout);
        ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT,
                ComponentContainer.LayoutConfig.MATCH_CONTENT);
        tableLayout.setRowCount(getrowcount());
        tableLayout.setColumnCount(getcolumncount());
        tableLayout.verifyLayoutConfig(layoutConfig);

        int width = (int) ResUtil.getDimen(mContext, ResourceTable.Float_md_colorchooser_circlesize);
        int height = (int) ResUtil.getDimen(mContext, ResourceTable.Float_md_colorchooser_circlesize);
        ComponentContainer.LayoutConfig cvlayout = new ComponentContainer.LayoutConfig(width, height);
        cvlayout.setMargins(20, 20, 20, 20);
        cvlayoutConfig = new DirectionalLayout.LayoutConfig(cvlayout);
        prepareCircleviews();

        return tableLayout;
    }

    @Override
    protected void onCreate() {
        super.onCreate();
        super.setSwipeToDismiss(true);
        setContentCustomComponent(mDialogLayout);
    }

    private Builder getBuilder() {
        return Builder.getBuilder();
    }

    public boolean isAccentMode() {
        return getBuilder().accentMode;
    }

    @Override
    protected void onHide() {
        super.onHide();
        if (callback != null) {
            callback.onColorChooserDismissed(this);
        }
    }

    @Override
    public void onClick(Component component) {
        DialogAction tag = (DialogAction) component.getTag();
        switch (tag) {
            case POSITIVE: {
                callback.onColorSelection(this, getSelectedColor());
                hide();
                break;
            }
            case NEGATIVE: {
                if (isInSub()) {
                    negativeButton.setText(ResUtil.getString(mContext, getBuilder().cancelBtn).toUpperCase());
                    isInSub(false);
                    subIndex(-1); // Do this to avoid ArrayIndexOutOfBoundsException
                    invalidate();
                } else {
                    hide();
                }
                break;
            }
            case NEUTRAL: {
                toggleCustom();
                break;
            }
        }

    }

    public static class Builder implements Serializable {
        public static Builder mbuilder;
        Context context;
        String mediumFont;
        String regularFont;
        final int title;
        int titleSub;
        int preselectColor;
        int doneBtn = ResourceTable.String_md_done_label;
        int backBtn = ResourceTable.String_md_back_label;
        int cancelBtn = ResourceTable.String_md_cancel_label;
        int customBtn = ResourceTable.String_md_custom_label;
        int presetsBtn = ResourceTable.String_md_presets_label;
        Color[] colorsTop;
        Color[][] colorsSub;
        String tag;
        Theme theme;

        boolean accentMode = false;
        boolean dynamicButtonColor = true;
        boolean allowUserCustom = true;
        boolean allowUserCustomAlpha = true;
        boolean setPreselectionColor = false;

        public Builder(Context context, int title) {
            this.context = context;
            this.title = title;
            mbuilder = this;

            if (context instanceof ColorCallback) {
                callback = (ColorCallback) context;
            }
        }

        public static Builder getBuilder() {
            return mbuilder;
        }


        public Builder typeface(String medium, String regular) {
            this.mediumFont = medium;
            this.regularFont = regular;
            return this;
        }


        public Builder titleSub(int titleSub) {
            this.titleSub = titleSub;
            return this;
        }


        public Builder tag(String tag) {
            this.tag = tag;
            return this;
        }


        public Builder theme(Theme theme) {
            this.theme = theme;
            return this;
        }


        public Builder preselect(int preselect) {
            preselectColor = preselect;
            setPreselectionColor = true;
            return this;
        }


        public Builder accentMode(boolean accentMode) {
            this.accentMode = accentMode;
            return this;
        }


        public Builder doneButton(int text) {
            doneBtn = text;
            return this;
        }


        public Builder backButton(int text) {
            backBtn = text;
            return this;
        }


        public Builder cancelButton(int text) {
            cancelBtn = text;
            return this;
        }


        public Builder customButton(int text) {
            customBtn = text;
            return this;
        }


        public Builder presetsButton(int text) {
            presetsBtn = text;
            return this;
        }


        public Builder dynamicButtonColor(boolean enabled) {
            dynamicButtonColor = enabled;
            return this;
        }


        public Builder customColors(Color[] topLevel, Color[][] subLevel) {
            colorsTop = topLevel;
            colorsSub = subLevel;
            return this;
        }

        public ColorChooserDialog build() {
            this.context = context;
            ColorChooserDialog dialog = new ColorChooserDialog(context);
            return dialog;
        }


        public ColorChooserDialog show() {
            ColorChooserDialog dialog = build();
            int orientation = context.getResourceManager().getConfiguration().direction;
            if (orientation == Configuration.DIRECTION_VERTICAL && dialog != null) {
                dialog.setSize(850, 1500);
            } else {
                dialog.setSize(850, 900);
            }
            dialog.show();
            return dialog;
        }
    }

    public interface ColorCallback {

        void onColorSelection(ColorChooserDialog dialog, int selectedColor);

        void onColorChooserDismissed(ColorChooserDialog dialog);
    }

}
